
#include "PostProcessingPipeline.h"
#include "D3D11Client.h"

CSPostProcessingPipeline::CSPostProcessingPipeline(D3D11Client * gc, CComPtrEx<ID3D11Texture2D>& inputTexture)
	:PostProcessingPipeline(gc, inputTexture)
{
	m_adaptedLuminance = m_currentLuminance = 0.5f;
	for(auto i = 0; i < LUMINANCE_HISTORY_LENGTH; i++)
	{
		m_luminanceHistory[i] = m_currentLuminance;
	}
}

CSPostProcessingPipeline::~CSPostProcessingPipeline()
{
}

bool CSPostProcessingPipeline::LoadComputeShaders()
{
	if (!GET_SHADER(GetClient(), "Modules\\D3D11Shaders\\HDR_CopyTextureWithExposure.hlsl", "CopyTextureWithExposure", processHDR_copyTexCS))
		return false;

	if (!GET_SHADER(GetClient(), "Modules\\D3D11Shaders\\HDR_CalculateLuminance.hlsl", "CalculateLuminance", processHDR_calcLumCS))
		return false;

	if (!GET_SHADER(GetClient(), "Modules\\D3D11Shaders\\HDR_Reduce.hlsl", "Reduce", processHDR_ReduceCS))
		return false;

	if (!GET_SHADER(GetClient(), "Modules\\D3D11Shaders\\HDR_CalculateBloomTexture.hlsl", "CalculateBloomTexture", processHDR_createBloomTextureCS))
		return false;

	if (!GET_SHADER(GetClient(), "Modules\\D3D11Shaders\\HDR_GaussianBlur.hlsl", "BloomTextureBlurVerticalPass", processHDR_verticalBlurCS))
		return false;

	if (!GET_SHADER(GetClient(), "Modules\\D3D11Shaders\\HDR_GaussianBlur.hlsl", "BloomTextureBlurHorizontalPass", processHDR_horizontalBlurCS))
		return false;

	if (!GET_SHADER(GetClient(), "Modules\\D3D11Shaders\\HDR_Downsample.hlsl", "Downsample", processHDR_downsampleCS))
		return false;

	if (!GET_SHADER(GetClient(), "Modules\\D3D11Shaders\\HDR_Upsample.hlsl", "Upsample", processHDR_upsampleCS))
		return false;
	return true;
}

bool CSPostProcessingPipeline::Init(CComPtrEx<ID3D11Texture2D>& outputTexture)
{
	if (!LoadComputeShaders())
		return false;

	if (!GET_SHADER(GetClient(), "Modules\\D3D11Shaders\\DisplayHDRTexture.fx", "VS", displayTextureVS))
		return false;
	if (!GET_SHADER(GetClient(), "Modules\\D3D11Shaders\\DisplayHDRTexture.fx", "PS_BUFFER", displayTexturePS))
		return false;

	BEGIN_LAYOUT(polygonLayout)
		DEFINE_LAYOUT_ELEMENT("POSITION", DXGI_FORMAT_R32G32B32A32_FLOAT, 0)
		DEFINE_LAYOUT_ELEMENT("TEXCOORD", DXGI_FORMAT_R32G32_FLOAT, 0)
	END_LAYOUT

	if (!GetClient()->CreateInputLayout(polygonLayout, displayTextureVS, displayTextureLayout))
		return false;

	//create texture for processed image - ready to be displayed
	D3D11_TEXTURE2D_DESC texDesc;
	ZeroMemory(&texDesc, sizeof(D3D11_TEXTURE2D_DESC));
	texDesc.ArraySize = 1;
	texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
	texDesc.Width = cfg->cWidth;
	texDesc.Height = cfg->cHeight;
	texDesc.MipLevels = 1;
	texDesc.SampleDesc.Count = 1;
	texDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
	HR( Dev->CreateTexture2D(&texDesc, nullptr, &outputTexture) );

	D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
	ZeroMemory(&rtvDesc, sizeof(D3D11_RENDER_TARGET_VIEW_DESC));
	rtvDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
	rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
	rtvDesc.Texture2D.MipSlice = 0;
	HR ( Dev->CreateRenderTargetView(outputTexture.p, &rtvDesc, &m_ppResult_RTV) );
	//m_ppResult
	
	HDRTarget_resolved = m_inputTexture;

	D3D11_BUFFER_DESC buffDesc;

	DISPLAY_TEXTURE_VERTEX quad [] = 
	{
		{ -1.0f, -1.0f, 0.0f, 1.0f,		0.0f, 1.0f},
		{ -1.0f,  1.0f, 0.0f, 1.0f,		0.0f, 0.0f},
		{  1.0f,  1.0f, 0.0f, 1.0f,		1.0f, 0.0f},
		{  1.0f, -1.0f, 0.0f, 1.0f,		1.0f, 1.0f}
	};

	D3D11_SUBRESOURCE_DATA initData;
	ZeroMemory(&initData, sizeof(D3D11_SUBRESOURCE_DATA));
	initData.pSysMem = quad;

	ZeroMemory(&buffDesc, sizeof(D3D11_BUFFER_DESC));
	buffDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	buffDesc.ByteWidth = 4 * sizeof(DISPLAY_TEXTURE_VERTEX);
	buffDesc.Usage = D3D11_USAGE_DEFAULT;
	HR( Dev->CreateBuffer(&buffDesc, &initData, &displayTextureVB) );

	UINT indices [] = { 0, 1, 2,  0, 2, 3 };
	initData.pSysMem = indices;

	ZeroMemory(&buffDesc, sizeof(D3D11_BUFFER_DESC));
	buffDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	buffDesc.ByteWidth = 6 * sizeof(UINT);
	buffDesc.Usage = D3D11_USAGE_DEFAULT;
	HR( Dev->CreateBuffer(&buffDesc, &initData, &displayTextureIB) );

	//create texture for processed image - ready to be displayed

	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
	ZeroMemory(&srvDesc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
	srvDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	srvDesc.Texture2D.MipLevels = 1;
	HR( Dev->CreateShaderResourceView(HDRTarget_resolved, &srvDesc, &HDRTarget_resolved_SRV) );
	
	//buffer for full bloom texture
	ZeroMemory(&buffDesc, sizeof(D3D11_BUFFER_DESC));
	buffDesc.StructureByteStride = sizeof(float) * 4;
	buffDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS;
	buffDesc.Usage = D3D11_USAGE_DEFAULT;
	buffDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
	buffDesc.ByteWidth = cfg->cWidth * cfg->cHeight * buffDesc.StructureByteStride;
	HR( Dev->CreateBuffer(&buffDesc, nullptr, &processHDR_BloomTextureBuffer_full) );
	//buffer for result texture
	HR( Dev->CreateBuffer(&buffDesc, nullptr, &processedTexture) );

	//buffers for downsampled bloom texture
	buffDesc.ByteWidth = (UINT)ceil((float)texDesc.Width / 2) * (UINT)ceil((float)texDesc.Height / 2) * buffDesc.StructureByteStride;

	HR( Dev->CreateBuffer(&buffDesc, nullptr, &processHDR_BloomTexture_downsampled) );
	HR( Dev->CreateBuffer(&buffDesc, nullptr, &processHDR_BloomTexture_downsampled_result) );

	//now SRVs
	ZeroMemory(&srvDesc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
	srvDesc.Format = DXGI_FORMAT_UNKNOWN;
	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
	srvDesc.Texture2D.MipLevels = 1;
	srvDesc.Buffer.ElementWidth = cfg->cWidth * cfg->cHeight;

	//for full bloom texture
	HR( Dev->CreateShaderResourceView(processHDR_BloomTextureBuffer_full, &srvDesc, &processHDR_BloomTextureBuffer_full_SRV) );
	//for end result texture
	HR( Dev->CreateShaderResourceView(processedTexture, &srvDesc, &processedTexture_SRV) );
	
	srvDesc.Buffer.ElementWidth = (UINT)ceil((float)texDesc.Width / 2) * (UINT)ceil((float)texDesc.Height / 2);
	//downsampled bloom texture
	HR( Dev->CreateShaderResourceView(processHDR_BloomTexture_downsampled, &srvDesc, &processHDR_BloomTexture_downsampled_SRV) );
	HR( Dev->CreateShaderResourceView(processHDR_BloomTexture_downsampled_result, &srvDesc, &processHDR_BloomTexture_downsampled_result_SRV) );

	//now UAVs

	D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
	ZeroMemory(&uavDesc, sizeof(D3D11_UNORDERED_ACCESS_VIEW_DESC));
	uavDesc.Format = DXGI_FORMAT_UNKNOWN;
	uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
	uavDesc.Buffer.NumElements = cfg->cWidth * cfg->cHeight;
	//for end result texture
	HR( Dev->CreateUnorderedAccessView(processedTexture, &uavDesc, &processedTexture_UAV) );
	//for final bloom texture
	HR( Dev->CreateUnorderedAccessView(processHDR_BloomTextureBuffer_full, &uavDesc, &processHDR_BloomTextureBuffer_full_UAV) );
	
	uavDesc.Buffer.NumElements = (UINT)ceil((float)texDesc.Width / 2) * (UINT)ceil((float)texDesc.Height / 2);
	
	//for downsampled result
	HR( Dev->CreateUnorderedAccessView(processHDR_BloomTexture_downsampled, &uavDesc, &processHDR_BloomTexture_downsampled_UAV) );
	HR( Dev->CreateUnorderedAccessView(processHDR_BloomTexture_downsampled_result, &uavDesc, &processHDR_BloomTexture_downsampled_result_UAV) );

	ZeroMemory(&buffDesc, sizeof(D3D11_BUFFER_DESC));
	buffDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	buffDesc.ByteWidth = sizeof(csCopyTextureBuffer);
	buffDesc.Usage = D3D11_USAGE_DEFAULT;
	
	HR( Dev->CreateBuffer(&buffDesc, nullptr, &processHDRCB) );
	
	int batchSizeX = (int)ceil((float)cfg->cWidth / 8);
	int batchSizeY = (int)ceil((float)cfg->cHeight / 8);
	ZeroMemory(&buffDesc, sizeof(D3D11_BUFFER_DESC));
	buffDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS;
	buffDesc.ByteWidth = sizeof(float) * batchSizeX * batchSizeY;
	buffDesc.Usage = D3D11_USAGE_DEFAULT;
	buffDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
	buffDesc.StructureByteStride = sizeof(float);
	
	HR( Dev->CreateBuffer(&buffDesc, nullptr, &processHDR_calcLumBuffer_input) );
	HR( Dev->CreateBuffer(&buffDesc, nullptr, &processHDR_calcLumBuffer_result) );

	buffDesc.Usage = D3D11_USAGE_STAGING;
	buffDesc.BindFlags = 0;
	buffDesc.ByteWidth = sizeof(float);
	buffDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;
	
	HR( Dev->CreateBuffer(&buffDesc, nullptr, &processHDR_calcLumBufferRW) );

	ZeroMemory(&srvDesc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
	srvDesc.Format = DXGI_FORMAT_UNKNOWN;
	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
	srvDesc.Buffer.ElementWidth = batchSizeX * batchSizeY;
	HR( Dev->CreateShaderResourceView(processHDR_calcLumBuffer_input, &srvDesc, &processHDR_calcLumBuffer_input_SRV) );
	HR( Dev->CreateShaderResourceView(processHDR_calcLumBuffer_result, &srvDesc, &processHDR_calcLumBuffer_result_SRV) );

	ZeroMemory(&uavDesc, sizeof(D3D11_UNORDERED_ACCESS_VIEW_DESC));
	uavDesc.Format = DXGI_FORMAT_UNKNOWN;
	uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
	uavDesc.Buffer.NumElements = batchSizeX * batchSizeY;
	HR( Dev->CreateUnorderedAccessView(processHDR_calcLumBuffer_input, &uavDesc, &processHDR_calcLumBuffer_input_UAV) );
	HR( Dev->CreateUnorderedAccessView(processHDR_calcLumBuffer_result, &uavDesc, &processHDR_calcLumBuffer_result_UAV) );

	return true;
}

void CSPostProcessingPipeline::RunPipeline(double interval, bool isPause)
{
	
	ID3D11Buffer * emptyBuffers[] = { nullptr };
	ID3D11ShaderResourceView * emptySRVs [] = { nullptr };
	ID3D11UnorderedAccessView * emptyUAVs [] = { nullptr };
	
	int batchSizeX = (int)ceil((float)cfg->cWidth / 8);
	int batchSizeY = (int)ceil((float)cfg->cHeight / 8);
	int totalPixelsCount = cfg->cWidth * cfg->cHeight;

	csCopyTextureBuffer cbCopyTextureBuffer;
	ZeroMemory(&cbCopyTextureBuffer, sizeof(csCopyTextureBuffer));
	cbCopyTextureBuffer.textureSize[0] = cfg->cWidth;
	cbCopyTextureBuffer.textureSize[1] = cfg->cHeight;
	cbCopyTextureBuffer.dispatchDimensions[0] = batchSizeX;
	cbCopyTextureBuffer.dispatchDimensions[1] = batchSizeY;
	iCtx->UpdateSubresource(processHDRCB, 0, nullptr, &cbCopyTextureBuffer, 0, 0);

	//stage 1 - reduce original texture to 1D buffer to calculate luminance
	iCtx->CSSetConstantBuffers(0, 1, &processHDRCB);
	iCtx->CSSetShaderResources(0, 1, &HDRTarget_resolved_SRV);
	iCtx->CSSetUnorderedAccessViews(0, 1, &processHDR_calcLumBuffer_result_UAV, nullptr);
	iCtx->CSSetShader(processHDR_calcLumCS, nullptr, 0);
	iCtx->Dispatch(batchSizeX, batchSizeY, 1);

	ID3D11ShaderResourceView * currentSRV[] = { processHDR_calcLumBuffer_result_SRV };
	ID3D11UnorderedAccessView * currentUAV[] = { processHDR_calcLumBuffer_input_UAV };
	auto secondInput = true;
		
	//now original texture has been reduced to (batchSizeX * batchSizeY, 1)
	//we need to keep reducing it until it will become (1, 1)
	iCtx->CSSetUnorderedAccessViews(0, 1, emptyUAVs, nullptr);
	iCtx->CSSetShaderResources(0, 1, currentSRV);
	iCtx->CSSetUnorderedAccessViews(0, 1, currentUAV, nullptr);
	iCtx->CSSetShader(processHDR_ReduceCS, nullptr, 0);
	cbCopyTextureBuffer.inputsCount = batchSizeX * batchSizeY;
	batchSizeX = (int)ceil((float)batchSizeX * batchSizeY / 128);
	while (batchSizeX > 1)
	{
		cbCopyTextureBuffer.dispatchDimensions[0] = batchSizeX;
		cbCopyTextureBuffer.dispatchDimensions[1] = 1;
		iCtx->UpdateSubresource(processHDRCB, 0, nullptr, &cbCopyTextureBuffer, 0, 0);
		iCtx->Dispatch(batchSizeX, 1, 1);
		secondInput = !secondInput;
		currentSRV[0] = secondInput ? processHDR_calcLumBuffer_result_SRV : processHDR_calcLumBuffer_input_SRV;
		currentUAV[0] = secondInput ? processHDR_calcLumBuffer_input_UAV : processHDR_calcLumBuffer_result_UAV;
		iCtx->CSSetUnorderedAccessViews(0, 1, emptyUAVs, nullptr);
		iCtx->CSSetShaderResources(0, 1, currentSRV);
		iCtx->CSSetUnorderedAccessViews(0, 1, currentUAV, nullptr);
		cbCopyTextureBuffer.inputsCount = batchSizeX;
		batchSizeX = (int)ceil((float)batchSizeX / 128);
	}
	cbCopyTextureBuffer.dispatchDimensions[0] = batchSizeX;
	cbCopyTextureBuffer.dispatchDimensions[1] = 1;
	iCtx->UpdateSubresource(processHDRCB, 0, nullptr, &cbCopyTextureBuffer, 0, 0);
	iCtx->Dispatch(batchSizeX, 1, 1);

	//reduction is completed, retrieve results
	ID3D11Buffer * resultBuffer = secondInput ? processHDR_calcLumBuffer_input : processHDR_calcLumBuffer_result;
	D3D11_BOX box;
	box.left = 0; 
	box.right = 4;
	box.bottom = 1;
	box.top = 0;
	box.front = 0;
	box.back = 1;
	iCtx->CopySubresourceRegion(processHDR_calcLumBufferRW, 0, 0, 0, 0, resultBuffer, 0, &box);
	//iCtx->CopyResource(processHDR_calcLumBufferRW, secondInput ? processHDR_calcLumBuffer_input : processHDR_calcLumBuffer_result);

	D3D11_MAPPED_SUBRESOURCE resource;
	iCtx->Map(processHDR_calcLumBufferRW, 0, D3D11_MAP_READ, 0, &resource);
	float * pTotalPixels = (float *)resource.pData;
	float luminance = (*pTotalPixels) / ((float)totalPixelsCount);
	iCtx->Unmap(processHDR_calcLumBufferRW, 0);
	volatile float vluminance = luminance;
	if (vluminance != vluminance)
	{
		luminance = 0.5f;
	}
	CLAMP_VALUE(luminance, 0.15f, 20.0f);
	CLAMP_VALUE(m_currentLuminance, 0.15f, 20.0f);
	
	static const float tau = 0.3f;
	if (!isPause)
	{
		auto timeStep = interval;
		auto lumDiff = m_currentLuminance - luminance;
		bool increasing = lumDiff > 0;
		int total = 0;
		for(UINT i = 0; i < LUMINANCE_HISTORY_LENGTH; i++)
		{
			if (m_luminanceHistory[i] > luminance)
			{
				total += 1;
			}
		}
		if (total == 0 || total == 16)
		{
			luminance = m_currentLuminance + (luminance - m_currentLuminance) * float(1 - exp(-timeStep * tau));
		}
		else
		{
			luminance = m_currentLuminance;
		}
		m_currentLuminance = luminance;
	}
	else
	{
		luminance = m_currentLuminance;
	}
	memcpy(m_luminanceHistory, &m_luminanceHistory[1], sizeof(float) * (LUMINANCE_HISTORY_LENGTH - 1));
	m_luminanceHistory[LUMINANCE_HISTORY_LENGTH - 1] = luminance;

	cbCopyTextureBuffer.exposure = 1.0f - luminance;

	//now creating bloom texture
	batchSizeX = (int)ceil((float)cfg->cWidth / 8);
	batchSizeY = (int)ceil((float)cfg->cHeight / 8);	
	cbCopyTextureBuffer.dispatchDimensions[0] = batchSizeX;
	cbCopyTextureBuffer.dispatchDimensions[1] = batchSizeY;
	iCtx->UpdateSubresource(processHDRCB, 0, nullptr, &cbCopyTextureBuffer, 0, 0);
	
	iCtx->CSSetShaderResources(0, 1, &HDRTarget_resolved_SRV);
	iCtx->CSSetUnorderedAccessViews(0, 1, &processHDR_BloomTextureBuffer_full_UAV, nullptr);
	iCtx->CSSetShader(processHDR_createBloomTextureCS, nullptr, 0);
	iCtx->Dispatch(batchSizeX, batchSizeY, 1);
	
	iCtx->CSSetUnorderedAccessViews(0, 1, emptyUAVs, nullptr);

	//now downsample bloom texture
	cbCopyTextureBuffer.secondTextureSize[0] = (UINT)ceil((float)cfg->cWidth / 2);
	cbCopyTextureBuffer.secondTextureSize[1] = (UINT)ceil((float)cfg->cHeight / 2);
	batchSizeX = (int)ceil((float)cbCopyTextureBuffer.secondTextureSize[0] / 8);
	batchSizeY = (int)ceil((float)cbCopyTextureBuffer.secondTextureSize[1] / 8);	
	cbCopyTextureBuffer.dispatchDimensions[0] = batchSizeX;
	cbCopyTextureBuffer.dispatchDimensions[1] = batchSizeY;
	iCtx->UpdateSubresource(processHDRCB, 0, nullptr, &cbCopyTextureBuffer, 0, 0);
	iCtx->CSSetShaderResources(0, 1, &processHDR_BloomTextureBuffer_full_SRV);
	iCtx->CSSetUnorderedAccessViews(0, 1, &processHDR_BloomTexture_downsampled_result_UAV, nullptr);
	iCtx->CSSetShader(processHDR_downsampleCS, nullptr, 0);
	iCtx->Dispatch(batchSizeX, batchSizeY, 1);

	currentSRV[0] = processHDR_BloomTexture_downsampled_result_SRV;
	currentUAV[0] = processHDR_BloomTexture_downsampled_UAV;
	iCtx->CSSetUnorderedAccessViews(0, 1, emptyUAVs, nullptr);
	iCtx->CSSetShaderResources(0, 1, currentSRV);
	iCtx->CSSetUnorderedAccessViews(0, 1, currentUAV, nullptr);
	//iCtx->CopyResource(processHDR_BloomTexture_downsampled, processHDR_BloomTexture_downsampled_result);

	//iCtx->CSSetUnorderedAccessViews(0, 1, emptyUAVs, nullptr);

	//performing gaussian blur on downsampled bloom texture
	static const int kernelhalf = 8;
	//horizontal pass
	//each thread groups is of (128, 1, 1) size, so we need (ceil(width / 128 - kernelhalf / 2), height) thread groups
	cbCopyTextureBuffer.textureSize[0] = cbCopyTextureBuffer.secondTextureSize[0];
	cbCopyTextureBuffer.textureSize[1] = cbCopyTextureBuffer.secondTextureSize[1];
	batchSizeX = (int)ceil((float)cbCopyTextureBuffer.textureSize[0] / (128  - kernelhalf * 2));
	batchSizeY = cbCopyTextureBuffer.textureSize[1];
	cbCopyTextureBuffer.dispatchDimensions[0] = batchSizeX;
	cbCopyTextureBuffer.dispatchDimensions[1] = batchSizeY;
	iCtx->UpdateSubresource(processHDRCB, 0, nullptr, &cbCopyTextureBuffer, 0, 0);
	/*iCtx->CSSetShaderResources(0, 1, &processHDR_BloomTexture_downsampled_SRV);
	iCtx->CSSetUnorderedAccessViews(0, 1, &processHDR_BloomTexture_downsampled_result_UAV, nullptr);*/
	currentSRV[0] = processHDR_BloomTexture_downsampled_result_SRV;
	currentUAV[0] = processHDR_BloomTexture_downsampled_UAV;
	iCtx->CSSetUnorderedAccessViews(0, 1, emptyUAVs, nullptr);
	iCtx->CSSetShaderResources(0, 1, currentSRV);
	iCtx->CSSetUnorderedAccessViews(0, 1, currentUAV, nullptr);

	iCtx->CSSetShader(processHDR_horizontalBlurCS, nullptr, 0);
	iCtx->Dispatch(batchSizeX, batchSizeY, 1);
	
	//iCtx->CopyResource(processHDR_BloomTexture_downsampled, processHDR_BloomTexture_downsampled_result);

	//vertical pass
	//each thread groups is of (128, 1, 1) size, so we need (width, ceil(height / 128 - kernelhalf / 2)) thread groups
	batchSizeX = cbCopyTextureBuffer.textureSize[0];
	batchSizeY = (int)ceil((float)cbCopyTextureBuffer.textureSize[1] / (128  - kernelhalf * 2));
	cbCopyTextureBuffer.dispatchDimensions[0] = batchSizeX;
	cbCopyTextureBuffer.dispatchDimensions[1] = batchSizeY;
	iCtx->UpdateSubresource(processHDRCB, 0, nullptr, &cbCopyTextureBuffer, 0, 0);

	currentSRV[0] = processHDR_BloomTexture_downsampled_SRV;
	currentUAV[0] = processHDR_BloomTexture_downsampled_result_UAV;
	iCtx->CSSetUnorderedAccessViews(0, 1, emptyUAVs, nullptr);
	iCtx->CSSetShaderResources(0, 1, currentSRV);
	iCtx->CSSetUnorderedAccessViews(0, 1, currentUAV, nullptr);

	iCtx->CSSetShader(processHDR_verticalBlurCS, nullptr, 0);
	iCtx->Dispatch(batchSizeX, batchSizeY, 1);
	
	//iCtx->CopyResource(processHDR_BloomTexture_downsampled, processHDR_BloomTexture_downsampled_result);

	//done, now upsampling bloom texture back to original size
	batchSizeX = (int)ceil((float)cbCopyTextureBuffer.secondTextureSize[0] / 8);
	batchSizeY = (int)ceil((float)cbCopyTextureBuffer.secondTextureSize[1] / 8);	
	cbCopyTextureBuffer.secondTextureSize[0] = cfg->cWidth;
	cbCopyTextureBuffer.secondTextureSize[1] = cfg->cHeight;
	cbCopyTextureBuffer.dispatchDimensions[0] = batchSizeX;
	cbCopyTextureBuffer.dispatchDimensions[1] = batchSizeY;
	iCtx->UpdateSubresource(processHDRCB, 0, nullptr, &cbCopyTextureBuffer, 0, 0);
	iCtx->CSSetUnorderedAccessViews(0, 1, emptyUAVs, nullptr);
	iCtx->CSSetShaderResources(0, 1, &processHDR_BloomTexture_downsampled_result_SRV);
	iCtx->CSSetUnorderedAccessViews(0, 1, &processHDR_BloomTextureBuffer_full_UAV, nullptr);
	iCtx->CSSetShader(processHDR_upsampleCS, nullptr, 0);
	iCtx->Dispatch(batchSizeX, batchSizeY, 1);
	
	iCtx->CSSetUnorderedAccessViews(0, 1, emptyUAVs, nullptr);

	//done, now process texture using calculated exposure and adding bloom texture
	
	cbCopyTextureBuffer.textureSize[0] = cbCopyTextureBuffer.secondTextureSize[0];
	cbCopyTextureBuffer.textureSize[1] = cbCopyTextureBuffer.secondTextureSize[1];
	batchSizeX = (int)ceil((float)cfg->cWidth / 8);
	batchSizeY = (int)ceil((float)cfg->cHeight / 8);	
	cbCopyTextureBuffer.dispatchDimensions[0] = batchSizeX;
	cbCopyTextureBuffer.dispatchDimensions[1] = batchSizeY;
	iCtx->UpdateSubresource(processHDRCB, 0, nullptr, &cbCopyTextureBuffer, 0, 0);

	iCtx->CSSetShaderResources(0, 1, &HDRTarget_resolved_SRV);
	iCtx->CSSetShaderResources(1, 1, &processHDR_BloomTextureBuffer_full_SRV);
	iCtx->CSSetUnorderedAccessViews(0, 1, &processedTexture_UAV, nullptr);
	iCtx->CSSetShader(processHDR_copyTexCS, nullptr, 0);

	iCtx->Dispatch(batchSizeX, batchSizeY, 1);

	//cleanup
	iCtx->CSSetConstantBuffers(0, 1, emptyBuffers);
	iCtx->CSSetShaderResources(0, 1, emptySRVs);
	iCtx->CSSetShaderResources(1, 1, emptySRVs);
	iCtx->CSSetUnorderedAccessViews(0, 1, emptyUAVs, nullptr);
	iCtx->CSSetShader(nullptr, nullptr, 0);


	UINT stride = sizeof(DISPLAY_TEXTURE_VERTEX);
	UINT offset = 0;
	iCtx->OMSetRenderTargets( 1, &m_ppResult_RTV, nullptr );
	iCtx->OMSetDepthStencilState(DSS_NoDepth_NoStencil, 0);
	iCtx->OMSetBlendState(BS_NoBlend, nullptr, 0xffffffff);

	iCtx->IASetInputLayout(displayTextureLayout);
	iCtx->IASetVertexBuffers(0, 1, &displayTextureVB, &stride, &offset);
	iCtx->IASetIndexBuffer(displayTextureIB, DXGI_FORMAT_R32_UINT, 0);
	iCtx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	iCtx->VSSetShader(displayTextureVS, nullptr, 0);
	iCtx->PSSetConstantBuffers(0, 1, &processHDRCB);
	iCtx->PSSetShaderResources(0, 1, &processedTexture_SRV);
	iCtx->PSSetShader(displayTexturePS, nullptr, 0);

	//iCtx->RSSetState(RS_CullBack_Solid);

	iCtx->DrawIndexed(6, 0, 0);
}